import { Col, Form, FormInstance, FormItemProps, Row } from 'antd'
import React, { useEffect, useImperativeHandle, useMemo, useState } from 'react'
import { StandardSearchButtonGroup } from '@/components'
import { Breakpoints, injectAntdComponent, SizeWatch } from '@/components/util'

/**
 * 查询字段
 */
export interface SearchFormItem extends FormItemProps {
  /** 字段名 */
  name?: string
  /** 文本 */
  label?: string
  /** 表单输入组件 */
  children: React.ReactElement
}

/**
 * 列表页查询条件组件属性
 */
export interface SearchFormProps {
  /**
   * 查询字段
   */
  items: SearchFormItem[]
  /**
   * 查询方法
   * @param form 表单实例
   */
  onSearch?: (form: FormInstance<any>) => Promise<void>
}

/**
 * 查询条件内部方法
 */
export type SearchFormRef = {
  /**
   * 表单实例
   */
  form: FormInstance
  /**
   * 校验并获取表单
   */
  validateFields: () => Promise<any>
  /**
   * 重置查询条件
   */
  reset: () => void
  /**
   * 设置状态状态
   * @param expand 展开状态
   */
  setExpand: (expand: boolean) => void
}

/**
 * 查询表单，宽度自适应调整
 */
export const SearchForm = (
  props: SearchFormProps & { ref: React.RefObject<SearchFormRef | null> }
) => (
  // 通过SizeWatch 包装以获得对宽度变化的感应
  <SizeWatch {...props}>
    <SearchFormComponent {...props} />
  </SizeWatch>
)

/**
 * 查询表单组件实现
 */
const SearchFormComponent = (
  props: SearchFormProps & {
    ref: React.RefObject<SearchFormRef | null>
    parentWidth?: number
  }
) => {
  const [form] = Form.useForm()
  const [expand, setExpand] = useState(false)
  const [colSize, setColSize] = useState(4)

  const { parentWidth, items = [], onSearch, ref } = props

  /**
   * 表单验证并获取字段
   */
  const validateFields = async () => {
    return await form.validateFields()
  }

  // 暴露内部接口
  useImperativeHandle(ref, () => ({
    form,
    validateFields: validateFields,
    reset: () => form.resetFields(),
    setExpand,
  }))

  // 根据宽度动态计算列数
  useEffect(() => {
    if (parentWidth) {
      if (parentWidth < Breakpoints.sm) {
        setColSize(2)
      } else if (parentWidth < Breakpoints.md) {
        setColSize(3)
      } else if (parentWidth < Breakpoints.xl) {
        setColSize(4)
      } else {
        setColSize(6)
      }
    }
  }, [parentWidth])

  // 构建表单字段
  const formItems = useMemo(() => {
    // 根据列数计算每列占据大小
    // 由于Items的长度可能小于列数，导致部分列空置，需要向下取数
    const colSpan = Math.floor(
      24 / Math.min(colSize, shrinkColSize(colSize, items.length + 1))
    )
    // 是否需要展示展开按钮
    // 若字段数大于列数+1(包含查询按钮)，则需要展示
    const showExpand = items.length > colSize - 1

    // 根据字段信息构建表单字段
    const formItems = items.map((item, index) => (
      <Col span={colSpan} key={index}>
        <Form.Item name={item.name} label={item.label} labelCol={{ span: 8 }}>
          {injectAntdComponent(item.children)}
        </Form.Item>
      </Col>
    ))

    // 在第一行的最后一个元素添加查询按钮组
    formItems.splice(
      colSize - 1,
      0,
      <Col span={colSpan} key="searchButtonGroup" className="search-button-col">
        <StandardSearchButtonGroup
          onSearch={() => onSearch?.(form)}
          onRest={() => form.resetFields()}
          showExpand={showExpand}
          expand={expand}
          onExpand={setExpand}
        />
      </Col>
    )

    return formItems
  }, [colSize, expand, form, items, onSearch])

  return (
    <Form form={form}>
      <Row gutter={24} wrap={expand}>
        {formItems}
      </Row>
    </Form>
  )
}

/**
 * 对列数进行收缩，令列数与元素数适应。
 *
 * 当列数在页面宽度下取到某个值如6时，实际的条件可能并没有5+1个，也许只有2个，这时候需要让列数缩小，用3列替代最为合适。
 *
 * @param colSize 计算出的列数
 * @param itemSize 元素数
 */
function shrinkColSize(colSize: number, itemSize: number) {
  if (itemSize >= colSize) return colSize
  else if (itemSize < 4 && itemSize <= 6) return 6
  else return itemSize
}
